/*
 * ObjModel.cpp
 *
 * This class represents a model loaded from a .obj file.
 * It has a simple obj loader, so the .obj file should look like this:
 *    - only verices, texture vertices, normals and faces
 *    - faces must be triangular
 *    - a seperate .mtl file with descriptions of materials, where each description ends with illum component
 *
 */

#include "ObjModel.h"
#include <gl/gl.h>
#include <gl/glu.h>
#include <string>
#include <iostream>

/*empty constructor*/
ObjModel::ObjModel() {

}


/*constructor to be used - reads files into data structures*/
ObjModel::ObjModel(char *pathname){
	readOBJ(pathname);
}


/*destructor: delete vector materials*/
ObjModel::~ObjModel() {
	for(unsigned int i=0; i<materials.size(); i++) {
		materials.at(i).normals.clear();
		materials.at(i).tex_vertices.clear();
		materials.at(i).vertices.clear();
	}
	materials.clear();
}


/*read an obj file into temporary vectors of vertices
 * and then copy them to correct material in vector of materials*/
void ObjModel::readOBJ(char *pathname){
	char line[100];
    Vertex v, vn;
    Vertex2D vt;
    std::vector<Vertex> temp_v, temp_vn;
    std::vector<Vertex2D> temp_vt;
    int v1,v2,v3,vt1,vt2,vt3,vn1,vn2,vn3; 		//indexes for faces
    int m = 0;									//index for material
    char mtl_file[30];
    char temp_material[50];
    char x = 'a';

	FILE *fp = fopen(pathname,"r");

    if (fp != NULL){
        while(fgets(line, 99, fp)){
        	if(line[0]=='m' && line[1]=='t'){			//"mtllib"
        		sscanf(line, "%*s %s", mtl_file);		//file with materials
        		readMTL(mtl_file);
        	}
        	else if(line[0] == 'v'){
        		if(line[1]=='t'){
        			sscanf(line, "%*s %f %f", &vt.x, &vt.y);		//texture vertices ("vt")
        			temp_vt.push_back(vt);
        		}
        		else if(line[1]=='n'){
        			sscanf(line, "%*s %f %f %f", &vn.x, &vn.y, &vn.z);		//normals ("vn")
        			temp_vn.push_back(vn);
        		}
        		else{
        			sscanf(line, "%*c %f %f %f", &v.x, &v.y, &v.z);		//vertices ("v")
        			temp_v.push_back(v);
        		}
        	}
        	else if(line[0]=='u' && line[1]=='s' && line[2]=='e'){		//"usemtl"
        		sscanf(line, "%*s %s", temp_material);					//material for following faces-triangles
        		for(unsigned int i=0; i<materials.size(); i++){
        			if(std::string(materials.at(i).name).compare(temp_material)==0){
        		    	m = i;											//set index
        		    	break;
        		    }
        		}
        	}

        	//faces ("f") - creating lists of vertices for fast rendering (for each material)
        	//watch out with indexes - obj starts count at 1, not 0!!!
        	else if(line[0]=='f'){
				sscanf(line, "%*c %*i%*c%c%*i%*s", &x);		//is model textured?
				if(x=='/'){
					sscanf(line, "%*c %i//%i %i//%i %i//%i", &v1,&vn1, &v2,&vn2, &v3,&vn3);
					materials.at(m).vertices.push_back(temp_v.at(v1-1));
					materials.at(m).vertices.push_back(temp_v.at(v2-1));
					materials.at(m).vertices.push_back(temp_v.at(v3-1));
					materials.at(m).normals.push_back(temp_vn.at(vn1-1));
					materials.at(m).normals.push_back(temp_vn.at(vn1-1));
					materials.at(m).normals.push_back(temp_vn.at(vn1-1));
				}
				else{
					sscanf(line, "%*c %i/%i/%i  %i/%i/%i %i/%i/%i", &v1,&vt1,&vn1, &v2,&vt2,&vn2, &v3,&vt3,&vn3);
					materials.at(m).vertices.push_back(temp_v.at(v1-1));
					materials.at(m).vertices.push_back(temp_v.at(v2-1));
					materials.at(m).vertices.push_back(temp_v.at(v3-1));
					materials.at(m).tex_vertices.push_back(temp_vt.at(vt1-1));
					materials.at(m).tex_vertices.push_back(temp_vt.at(vt2-1));
					materials.at(m).tex_vertices.push_back(temp_vt.at(vt3-1));
					materials.at(m).normals.push_back(temp_vn.at(vn1-1));
					materials.at(m).normals.push_back(temp_vn.at(vn1-1));
					materials.at(m).normals.push_back(temp_vn.at(vn1-1));
				}
        	}
        }
    }
    else printf("obj_file error\n");

    temp_v.clear();
    temp_vn.clear();
    temp_vt.clear();

    fclose(fp);
}


/*read mtl file into vector of materials*/
void ObjModel::readMTL(char *fName){
	char line[100];
	struct Material mat;
	std::string path = "data/";

	path.append(fName);
	FILE *fp = fopen(&path.at(0),"r");
	path = "data/";

	if(fp!=NULL){
		while(fgets(line,99,fp)){
			if(line[0]=='n' && line[1]=='e' && line[2]=='w'){		//"newmtl"
				sscanf(line,"%*s %s",mat.name);						//name of material
			}
			else if(line[0]=='N' && line[1]=='s'){
				sscanf(line,"%*s %f", &mat.Ns);
			}
			else if(line[0]=='K' && line[1]=='a'){
				sscanf(line,"%*s %f %f %f", &mat.Ka[0], &mat.Ka[1], &mat.Ka[2]);
			}
			else if(line[0]=='K' && line[1]=='d'){
				sscanf(line,"%*s %f %f %f", &mat.Kd[0], &mat.Kd[1], &mat.Kd[2]);
			}
			else if(line[0]=='K' && line[1]=='s'){
				sscanf(line,"%*s %f %f %f", &mat.Ks[0], &mat.Ks[1], &mat.Ks[2]);
			}
			else if(line[0]=='N' && line[1]=='i'){
				sscanf(line,"%*s %f", &mat.Ni);
			}
			else if(line[0]=='d'){
				sscanf(line,"%*s %f", &mat.d);
				mat.Ka[3]=mat.d;
				mat.Kd[3]=mat.d;
				mat.Ks[3]=mat.d;
			}
			else if(line[0]=='i' && line[1]=='l'){				//"illum"
				sscanf(line,"%*s %i", &mat.illum);				//last obligatory component

				//check if there is texture file and push material
				fgets(line,99,fp);
				if(line[0]=='m'){								//"map_Kd"
					sscanf(line,"%*s %s", mat.file);			//texture file
					path.append(mat.file);
					Model3D::buildTexture(&path.at(0), mat.texture);
					path = "data/";
				}
				materials.push_back(mat);
				mat.file[0]=0;					//texture flag for rendering
			}
		}
	}
	else printf("mtl_file error\n");

	fclose(fp);
}


/*draw obj model*/
void ObjModel::renderGL(){
	float ka[] = {0.2, 0.2, 0.2, 1.0};
	float kd[] = {0.8, 0.8, 0.8, 1.0};
	float ks[] = {0.0, 0.0, 0.0, 1.0};

	glEnable(GL_TEXTURE_2D);

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	for (unsigned int i=0; i<materials.size(); i++){
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, materials.at(i).Ka);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, materials.at(i).Kd);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, materials.at(i).Ks);

		if(materials.at(i).file[0] != 0){
			glBindTexture(GL_TEXTURE_2D, materials.at(i).texture);
		}
		else{
			glBindTexture(GL_TEXTURE_2D, 0);
		}

		glVertexPointer(3, GL_FLOAT, 0, &materials.at(i).vertices.at(0));
		glNormalPointer(GL_FLOAT, 0, &materials.at(i).normals.at(0));
		glTexCoordPointer(2, GL_FLOAT, 0, &materials.at(i).tex_vertices.at(0));

		glDrawArrays(GL_TRIANGLES, 0, materials.at(i).vertices.size());
    }

	glBindTexture(GL_TEXTURE_2D, 0);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ka);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, kd);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, ks);

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisable(GL_TEXTURE_2D);

}

